
package com.yadong.smarthome2.datastore;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;

import com.yadong.smarthome2.control.ControlRules;
import com.yadong.smarthome2.control.ControlRules.RuleBuilder;
import com.yadong.smarthome2.datastore.Tables.Rule;
import com.yadong.smarthome2.device.Device;
import com.yadong.smarthome2.device.Device.DeviceConnectionType;
import com.yadong.smarthome2.device.Device.DeviceState;
import com.yadong.smarthome2.device.DeviceManagerService;
import com.yadong.smarthome2.protocol2.ALog;
import com.yadong.smarthome2.protocol2.ResultProtocol.DeviceInfo.DeviceType;
import com.yadong.smarthome2.protocol2.ResultProtocol.DeviceInfo.Status;
import com.yadong.smarthome2.protocol2.ResultProtocol.Function;
import com.yadong.smarthome2.protocol2.ResultProtocol.Function.Argument;
import com.yadong.smarthome2.protocol2.Utils;

public class SmartHomeDbOperator {
    private static final Uri AUTHORITY =
            Uri.parse("content://" + SmartHomeProvider.AUTHORITY);
    private Context mContext;

    public SmartHomeDbOperator(Context context) {
        this.mContext = context;
    }

    private Uri createUri(String path) {
        Uri.Builder builder = AUTHORITY.buildUpon();
        builder.appendPath(path);
        return builder.build();
    }

    public boolean saveRule(ControlRules rule) {
        ContentValues contentValues = new ContentValues();
        //STATUSES,MIN,MAX,JUST_RIGHT,FUNCTION_CODE,PARAMETERS
        contentValues.put(Tables.Rule.STATUSES,
                Utils.statusesToString(rule.getStatuses()));
        contentValues.put(Tables.Rule.MIN, rule.getMin());
        contentValues.put(Tables.Rule.MAX, rule.getMax());
        contentValues.put(Tables.Rule.JUST_RIGHT, rule.getJustRight());
        contentValues.put(Tables.Rule.FUNCTION_CODE, rule.getFunctionCode());
        contentValues.put(Tables.Rule.PARAMETERS,
                Utils.parametersToString(rule.getParameters()));
        contentValues.put(Tables.Rule.DEVICE_ID, rule.getDevice().getId());
        contentValues.put(Tables.Rule.TARGET_DEVICE_ID,
                rule.getTargetDevice().getId());
        Uri ruleUri = mContext.getContentResolver()
                .insert(createUri(Tables.Rule.TABLE_NAME), contentValues);
        long ruleId = -1;
        try {
            ruleId = Long.parseLong(ruleUri.getLastPathSegment());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (ruleId == -1) {
            ALog.loge(this, " status insert error get uri is :" + ruleUri);
            return false;
        }
        return true;
    }

    public boolean updateDevice(Device device) {
        ContentValues contentValues = new ContentValues();
        contentValues.put(Tables.Device.CONNECTION_TYPE,
                device.getDeviceConnectionType().ordinal());
        contentValues.put(Tables.Device.NAME, device.getDeviceDescription());
        contentValues.put(Tables.Device.DESCRIPTION, device.getDeviceDescription());
        contentValues.put(Tables.Device.ADDRESS, device.getAddress());
        contentValues.put(Tables.Device.MAC, device.getMac());
        long updateId = mContext.getContentResolver().update(createUri(Tables.Device.TABLE_NAME),
                contentValues, Tables.Device.ID + "=?", new String[] {
                    device.getId() + ""
                });
        return updateId == -1;
    }

    private boolean saveFunction(Function function, long deviceId) {
        ContentValues contentValues = new ContentValues();
        // NAME, CODE, DEVICE_ID
        contentValues.put(Tables.Function.NAME, function.getDescription());
        contentValues.put(Tables.Function.CODE, function.getCode());
        contentValues.put(Tables.Function.DEVICE_ID, deviceId);
        Uri functionUri = mContext.getContentResolver()
                .insert(createUri(Tables.Function.TABLE_NAME), contentValues);
        long functionId = -1;
        try {
            functionId = Long.parseLong(functionUri.getLastPathSegment());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (functionId == -1) {
            ALog.loge(this, " status insert error get uri is :" + functionUri);
            return false;
        }
        for (Argument argument : function.getArgumentsList()) {
            if (!saveArument(argument, functionId))
                return false;
        }
        return true;

    }

    public List<ControlRules> getAllRules(DeviceManagerService deviceManagerService) {
        Cursor rulesCursor = mContext.getContentResolver()
                .query(createUri(Tables.Rule.TABLE_NAME), null, null, null, null);
        List<ControlRules> rules = new ArrayList<ControlRules>();
        while (rulesCursor != null && rulesCursor.moveToNext()) {
            RuleBuilder builder = new RuleBuilder();
            builder.setmDevice(deviceManagerService.getDeviceFromId(
                    rulesCursor.getLong(rulesCursor.getColumnIndex(Rule.DEVICE_ID))));
            builder.setmTargetDevice(deviceManagerService.getDeviceFromId(
                    rulesCursor.getLong(rulesCursor.getColumnIndex(Rule.TARGET_DEVICE_ID))));
            builder.setmMin(rulesCursor.getString(rulesCursor.getColumnIndex(Rule.MIN)));
            builder.setmMax(rulesCursor.getString(rulesCursor.getColumnIndex(Rule.MAX)));
            builder.setmJustRight(rulesCursor.getString(
                    rulesCursor.getColumnIndex(Rule.JUST_RIGHT)));
            builder.setmDoFunction(rulesCursor.getString(
                    rulesCursor.getColumnIndex(Rule.FUNCTION_CODE)));
            builder.setmStatus(Utils.stringToStatuses(
                    rulesCursor.getString(rulesCursor.getColumnIndex(Rule.STATUSES))));
            builder.setmParameters(Utils.stringToParameters(
                    rulesCursor.getString(rulesCursor.getColumnIndex(Rule.PARAMETERS))));
            rules.add(builder.build());
        }
        return rules;
    }

    public boolean saveArument(Argument argument, long functionId) {
        ContentValues contentValues = new ContentValues();
        // ID, NAME, TYPE, LIMIT, FUNCTION_ID
        contentValues.put(Tables.Argument.NAME, argument.getName());
        contentValues.put(Tables.Argument.TYPE, argument.getType());
        contentValues.put(Tables.Argument.LIMIT, argument.getLimit());
        contentValues.put(Tables.Argument.FUNCTION_ID, functionId);
        Uri argumentUri = mContext.getContentResolver()
                .insert(createUri(Tables.Argument.TABLE_NAME), contentValues);
        long argumentId = -1;
        try {
            argumentId = Long.parseLong(argumentUri.getLastPathSegment());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (argumentId == -1) {
            ALog.loge(this, " status insert error get uri is :" + argumentUri);
            return false;
        }
        return true;

    }

    private boolean saveStatus(Status status, long deviceId) {
        ContentValues contentValues = new ContentValues();
        // ID, NAME, TYPE, DEVICE_ID
        contentValues.put(Tables.Status.NAME, status.getName());
        contentValues.put(Tables.Status.TYPE, status.getType());
        contentValues.put(Tables.Status.DEVICE_ID, deviceId);
        Uri statusUri = mContext.getContentResolver()
                .insert(createUri(Tables.Status.TABLE_NAME), contentValues);
        long statusId = -1;
        try {
            statusId = Long.parseLong(statusUri.getLastPathSegment());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (statusUri == null || statusId == -1) {
            ALog.loge(this, " status insert error get uri is :" + statusUri);
            return false;
        }
        return true;
    }

    public long saveDevice(Device device) {
        ContentValues deviceContentValues = new ContentValues();
        // ID, TYPE, NAME, DESCRIPTION, ADDRESS, MAC
        deviceContentValues.put(Tables.Device.CONNECTION_TYPE,
                device.getDeviceConnectionType().ordinal());
        deviceContentValues.put(Tables.Device.NAME, device.getDeviceDescription());
        deviceContentValues.put(Tables.Device.DESCRIPTION, device.getDeviceDescription());
        deviceContentValues.put(Tables.Device.ADDRESS, device.getAddress());
        deviceContentValues.put(Tables.Device.MAC, device.getMac());
        deviceContentValues.put(Tables.Device.DEVICE_INFO_VERSION,
                device.getDeviceInfoVersion());
        deviceContentValues.put(Tables.Device.TYPE, device.getDeviceType().ordinal());
        Uri deviceUri = mContext.getContentResolver()
                .insert(createUri(Tables.Device.TABLE_NAME), deviceContentValues);
        long deviceId = -1;
        try {
            deviceId = Long.parseLong(deviceUri.getLastPathSegment());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (deviceId == -1) {
            ALog.loge(this, " deivce insert error get uri is :" + deviceUri);
            return deviceId;
        }
        if (device.getStatus() != null) {
            for (Status status : device.getStatus()) {
                if (!saveStatus(status, deviceId)) {
                    return deviceId;
                }
            }
        }
        if (device.getFunctions() != null) {
            for (Function function : device.getFunctions()) {
                if (!saveFunction(function, deviceId)) {
                    return deviceId;
                }
            }
        }
        return deviceId;
    }

    public List<Argument> getArgments(long functionId) {
        List<Argument> arguments = new ArrayList<Argument>();
        Cursor argumentsCurosr = null;
        try {
            argumentsCurosr = mContext.getContentResolver()
                    .query(createUri(Tables.Argument.TABLE_NAME), Tables.Argument.ALL_PROJECTION,
                            Tables.Argument.FUNCTION_ID + "=?", new String[] {
                                functionId + ""
                            },
                            null);
            Argument.Builder argumentBuilder = Argument.newBuilder();
            while (argumentsCurosr != null && argumentsCurosr.moveToNext()) {
                argumentBuilder.clear();
                // ID, NAME, TYPE, LIMIT, FUNCTION_ID
                String name = argumentsCurosr.getString(
                        argumentsCurosr.getColumnIndex(Tables.Argument.NAME));
                String type = argumentsCurosr.getString(
                        argumentsCurosr.getColumnIndex(Tables.Argument.TYPE));
                String limit = argumentsCurosr.getString(
                        argumentsCurosr.getColumnIndex(Tables.Argument.LIMIT));
                argumentBuilder.setName(name);
                argumentBuilder.setType(type);
                argumentBuilder.setLimit(limit);
                arguments.add(argumentBuilder.build());
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (argumentsCurosr != null) {
                argumentsCurosr.close();
            }
        }
        return arguments;
    }

    public List<Function> getFunctions(long deviceId) {
        List<Function> functions = new ArrayList<Function>();
        Cursor functionCursor = null;
        try {
            functionCursor = mContext.getContentResolver()
                    .query(createUri(Tables.Function.TABLE_NAME), Tables.Function.ALL_PROJECTION,
                            Tables.Function.DEVICE_ID + "=?", new String[] {
                                deviceId + ""
                            }, null);
            Function.Builder functionBuilder = Function.newBuilder();
            while (functionCursor != null && functionCursor.moveToNext()) {
                functionBuilder.clear();
                // ID, NAME, CODE, DEVICE_ID
                long id = functionCursor.getLong(functionCursor.getColumnIndex(Tables.Function.ID));
                String name = functionCursor.getString(
                        functionCursor.getColumnIndex(Tables.Function.NAME));
                String code = functionCursor.getString(
                        functionCursor.getColumnIndex(Tables.Function.CODE));
                functionBuilder.setDescription(name);
                functionBuilder.setCode(code);
                functionBuilder.addAllArguments(getArgments(id));
                functions.add(functionBuilder.build());
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (functionCursor != null) {
                functionCursor.close();
            }
        }
        return functions;
    }

    public List<Status> getStatus(long deviceId) {
        List<Status> status = new ArrayList<Status>();
        Cursor statusCursor = null;
        try {
            statusCursor = mContext.getContentResolver()
                    .query(createUri(Tables.Status.TABLE_NAME), Tables.Status.ALL_PROJECTION,
                            Tables.Status.DEVICE_ID + "=?", new String[] {
                                deviceId + ""
                            }, null);
            Status.Builder statusBuilder = Status.newBuilder();
            while (statusCursor != null && statusCursor.moveToNext()) {
                statusBuilder.clear();
                // NAME, TYPE
                String name = statusCursor.getString(
                        statusCursor.getColumnIndex(Tables.Status.NAME));
                String type = statusCursor.getString(
                        statusCursor.getColumnIndex(Tables.Status.TYPE));
                statusBuilder.setName(name);
                statusBuilder.setType(type);
                status.add(statusBuilder.build());
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (statusCursor != null) {
                statusCursor.close();
            }
        }
        return status;
    }

    public List<Device> getAllDevice() {
        List<Device> devices = new ArrayList<Device>();
        Cursor deviceCursor = null;
        try {
            Uri queryDeviceUri = createUri(Tables.Device.TABLE_NAME);
            deviceCursor = mContext.getContentResolver()
                    .query(queryDeviceUri, Tables.Device.ALL_PROJECTION, null, null, null);
            while (deviceCursor != null && deviceCursor.moveToNext()) {
                // ID, TYPE, NAME, DESCRIPTION, ADDRESS, MAC
                long id = deviceCursor.getLong(
                        deviceCursor.getColumnIndex(Tables.Device.ID));
                int type = deviceCursor.getInt(
                        deviceCursor.getColumnIndex(Tables.Device.CONNECTION_TYPE));
                String name = deviceCursor.getString(
                        deviceCursor.getColumnIndex(Tables.Device.NAME));
                String discription = deviceCursor.getString(
                        deviceCursor.getColumnIndex(Tables.Device.DESCRIPTION));
                String address = deviceCursor.getString(
                        deviceCursor.getColumnIndex(Tables.Device.ADDRESS));
                String mac = deviceCursor.getString(
                        deviceCursor.getColumnIndex(Tables.Device.MAC));
                int deviceType = deviceCursor.getInt(
                        deviceCursor.getColumnIndex(Tables.Device.TYPE));
                int deviceInfoVersion = deviceCursor.getInt(
                        deviceCursor.getColumnIndex(Tables.Device.DEVICE_INFO_VERSION));
                Device device = new Device(DeviceConnectionType.values()[type],
                        address, discription, mac);
                device.setId(id);
                device.init(DeviceType.values()[deviceType], deviceInfoVersion,
                        getFunctions(id), getStatus(id));
                device.setState(DeviceState.PAIRED);
                devices.add(device);
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (deviceCursor != null) {
                deviceCursor.close();
            }
        }
        return devices;
    }
}
